//
//  File.swift
//  hook
//
//  Created by Ink on 2024/1/25.
//

import Foundation

public let DefaultSuiteName = (Bundle.main.bundleIdentifier ?? "SuiteName") + ".Default"
public let StoreSuiteName = (Bundle.main.bundleIdentifier ?? "SuiteName") + ".Store"

public class UserDefaultBase {
    
    public let mKey: String
    public let mSuiteName: String?
    
    public init(suiteName:String? = nil, key: String) {
        mKey = key
        mSuiteName = suiteName ?? DefaultSuiteName
    }
    
    public var container: UserDefaults {
        return UserDefaults(suiteName: mSuiteName) ?? .standard
    }
}

public extension UserDefaults {
    
    struct DefaultSuiteStored {
        
        @UserDefault("\(Self.self).keys")
        public static var keys: [String] = []
        
        public static func add<T>(data: UserDefault<T>) {
            if data.mSuiteName == DefaultSuiteName {
                add(key: data.mKey)
            }
        }
        
        private static func add(key: String) {
            if Self.keys.contains(key) != true, key != $keys.mKey {
                keys.append(key)
            }
        }
        
        public static func remove(key: String) {
            keys.removeAll(where: { $0 == key })
        }
    }
    
    static var DefaultSuiteNameStandard: UserDefaults {
        return UserDefaults(suiteName: DefaultSuiteName) ?? .standard
    }
}

@propertyWrapper
public class UserDefault<T>: UserDefaultBase {
    
    private let defaultValue: T
    
    public init(wrappedValue: T, suiteName:String? = nil, _ key: String) {
        defaultValue = wrappedValue
        super.init(suiteName: suiteName, key: key)
    }
    
    public var wrappedValue: T {
        get { container.object(forKey: mKey) as? T ?? defaultValue }
        set{
            if deepUnwrap(newValue) == nil {
                container.removeObject(forKey:mKey)
                UserDefaults.DefaultSuiteStored.remove(key: mKey)
            } else {
                container.set(newValue,forKey:mKey)
                UserDefaults.DefaultSuiteStored.add(data: self)
            }
        }
    }
    
    public var projectedValue: UserDefault<T> {
        return self
    }

}

/// 支持 options
public extension UserDefault where T : ExpressibleByNilLiteral {
    convenience init(suiteName:String? = nil, _ key: String) {
        self.init(wrappedValue: nil, suiteName:suiteName, key)
    }
}

/// eg.         let a = Clamped(wrappedValue: 123, range: 0...100)
@propertyWrapper
public class Clamped<T: Comparable> {
    
    public var wrappedValue: T

    init(wrappedValue: T, range: ClosedRange<T>) {
        self.wrappedValue = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
}

@propertyWrapper
public class UserDefaultAstrictComparable<T:Codable> : UserDefault<T> where T : Comparable{
    
    private var range: ClosedRange<T>
    
    public override var wrappedValue: T {
        get { super.wrappedValue }
        set { super.wrappedValue = max(min(newValue, range.lowerBound), range.upperBound) }
    }
    
    public init(wrappedValue: T, suiteName:String? = nil,key: String, _ range: ClosedRange<T>) {
        
        self.range = range
        super.init(wrappedValue: wrappedValue, suiteName:suiteName, key)
        self.wrappedValue = wrappedValue
    }
}
